package ciyuan.Player.Skill;

import ciyuan.Item.TypeInterface.SkillBookType;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.projectile.ProjectileHelper;
import net.minecraft.util.math.*;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.World;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;

import java.util.concurrent.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.BiConsumer;

/**
 * @author 150149
 */
@Mod.EventBusSubscriber
public class SkillUtils {

    public static final ThreadPoolExecutor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(5, 50, 10000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(200), new ThreadFactory() {
        @Override
        public Thread newThread(Runnable r) {
            return new Thread(r);
        }
    });

    private static final ConcurrentLinkedQueue<Entity> removeEntities = new ConcurrentLinkedQueue<Entity>();

    private static final ConcurrentLinkedQueue<Entity> addEntities = new ConcurrentLinkedQueue<Entity>();
    private static final ConcurrentLinkedQueue<World> addWorld = new ConcurrentLinkedQueue<World>();

    private static final Lock lock = new ReentrantLock();

    @SubscribeEvent
    public static void onTick(TickEvent event) {
        if (!removeEntities.isEmpty()) {
            Entity entity = removeEntities.poll();
            if (entity!=null) {
                entity.remove();
            }
        }

        if (!addEntities.isEmpty()) {
            if (lock.tryLock()) {
                Entity entity = addEntities.poll();
                World world = addWorld.poll();
                lock.unlock();
                world.addFreshEntity(entity);
            }
        }
    }


    public static void removeEntity(Entity entity) {
        removeEntities.add(entity);
    }

    public static void addEntity(Entity entity,World world) {
        try {
            lock.lock();
            addEntities.add(entity);
            addWorld.add(world);
        }finally {
            lock.unlock();
        }
    }

    private static final CopyOnWriteArrayList<HitInfo> hitListener = new CopyOnWriteArrayList<>();

    public static class HitInfo {
        public PlayerEntity playerEntity;
        public BiConsumer<PlayerEntity,Entity> consumer;
        public int lastTicks;

        public HitInfo(PlayerEntity playerEntity, BiConsumer<PlayerEntity, Entity> consumer, int lastTicks) {
            this.playerEntity = playerEntity;
            this.consumer = consumer;
            this.lastTicks = lastTicks;
        }
    }

    private static boolean canHitEntity(Entity entity) {
        return true;
    }

    @SubscribeEvent
    public static void onPlayerTick(TickEvent.PlayerTickEvent event) {
        if (hitListener.isEmpty()) {
            return;
        }

        for (HitInfo hitInfo:hitListener) {
            if (hitInfo.lastTicks<=0) {
                hitListener.remove(hitInfo);
                continue;
            }
            RayTraceResult raytraceresult = ProjectileHelper.getHitResult(hitInfo.playerEntity,SkillUtils::canHitEntity);
            if (raytraceresult.getType() == RayTraceResult.Type.ENTITY) {
                hitInfo.consumer.accept(hitInfo.playerEntity,((EntityRayTraceResult)raytraceresult).getEntity());
                hitListener.remove(hitInfo);
                break;
            }
            hitInfo.lastTicks--;
        }

    }

    public static void addHitListener(PlayerEntity playerEntity,BiConsumer<PlayerEntity,Entity> consumer,int lastTick) {
        hitListener.add(new HitInfo(playerEntity,consumer,lastTick));
    }

    public static void sleep(long time) {
        try {
            Thread.sleep(time);
        } catch (Exception ignored) {
        }
    }

    public static BlockRayTraceResult getPlayerSee(World world, PlayerEntity playerEntity, RayTraceContext.FluidMode p_219968_2_, int distance) {
        float f = playerEntity.xRot;
        float f1 = playerEntity.yRot;
        Vector3d vector3d = playerEntity.getEyePosition(1.0F);
        float f2 = MathHelper.cos(-f1 * ((float) Math.PI / 180F) - (float) Math.PI);
        float f3 = MathHelper.sin(-f1 * ((float) Math.PI / 180F) - (float) Math.PI);
        float f4 = -MathHelper.cos(-f * ((float) Math.PI / 180F));
        float f5 = MathHelper.sin(-f * ((float) Math.PI / 180F));
        float f6 = f3 * f4;
        float f7 = f2 * f4;
        Vector3d vector3d1 = vector3d.add((double) f6 * (double) distance, (double) f5 * (double) distance, (double) f7 * (double) distance);
        return world.clip(new RayTraceContext(vector3d, vector3d1, RayTraceContext.BlockMode.OUTLINE, p_219968_2_, playerEntity));
    }


    public static void setSkillPercentage(PlayerEntity playerEntity,int currentValue,int maxValue) {
        if (playerEntity.level.isClientSide) {
            setSaveData(playerEntity, "skillPercentage", (int) ((currentValue / (float) maxValue) * 100.0));
        }
    }

    public static float getSkillPercentage(PlayerEntity playerEntity) {
        return (float) (getSaveData(playerEntity, "skillPercentage") / 100.0);
    }

    private static final ConcurrentHashMap<String, Long> saveData = new ConcurrentHashMap<>();

    public static Long getSaveData(PlayerEntity playerEntity, SkillBookType SkillBookType) {
        return saveData.getOrDefault(playerEntity.getName().getString() + "," + SkillBookType.getSkillCastName(), 0L);
    }

    public static Long getSaveData(PlayerEntity playerEntity, String name) {
        return saveData.getOrDefault(playerEntity.getName().getString() + "," + name, 0L);
    }

    public static void setSaveData(PlayerEntity playerEntity, SkillBookType SkillBookType, long value) {
        saveData.put(playerEntity.getName().getString() + "," + SkillBookType.getSkillCastName(), value);
    }

    public static void setSaveData(PlayerEntity playerEntity, String name, long value) {
        saveData.put(playerEntity.getName().getString() + "," + name, value);
    }

    private static final String SKILL_COLD = "THECOLDTIME";
    private static final String SKILL_CAST = "THECASTTIME";

    public static void setSkillColdInterval(PlayerEntity playerEntity, SkillBookType SkillBookType, long value) {
        setSaveData(playerEntity, SkillBookType.getSkillCastName() + SKILL_COLD, value);
    }

    public static void setSkillColdInterval(PlayerEntity playerEntity, String SkillBookTypeName, long value) {
        setSaveData(playerEntity, SkillBookTypeName + SKILL_COLD, value);
    }

    public static void saveSkillCastTime(PlayerEntity playerEntity, SkillBookType SkillBookType) {
        setSaveData(playerEntity, SkillBookType.getSkillCastName() + SKILL_CAST, System.currentTimeMillis());
    }

    public static void saveSkillCastTime(PlayerEntity playerEntity, String SkillBookTypeName) {
        setSaveData(playerEntity, SkillBookTypeName + SKILL_CAST, System.currentTimeMillis());
    }

    public static long getSkillCastTime(PlayerEntity playerEntity, SkillBookType SkillBookType) {
        return getSaveData(playerEntity, SkillBookType.getSkillCastName() + SKILL_CAST);
    }

    public static long getSkillCastTime(PlayerEntity playerEntity, String SkillBookTypeName) {
        return getSaveData(playerEntity, SkillBookTypeName + SKILL_CAST);
    }

    public static boolean canCastSkill(PlayerEntity playerEntity, SkillBookType SkillBookType) {
        long time = getSaveData(playerEntity, SkillBookType.getSkillCastName() + SKILL_COLD);
        System.out.println("技能:" + SkillBookType.getSkillCastName() + " 上次释放时间:" + getSkillCastTime(playerEntity, SkillBookType) + " 冷却时间：" + time + " 差值:" + (System.currentTimeMillis() - getSkillCastTime(playerEntity, SkillBookType)));
        return System.currentTimeMillis() - getSkillCastTime(playerEntity, SkillBookType) > time;
    }

    public static boolean canCastSkill(PlayerEntity playerEntity, String SkillBookTypeName) {
        long time = getSaveData(playerEntity, SkillBookTypeName + SKILL_COLD);
        return System.currentTimeMillis() - getSkillCastTime(playerEntity, SkillBookTypeName) > time;
    }
}
